home *** CD-ROM | disk | FTP | other *** search
-
-
-
- 177
-
- CHAPTER 17 - INTERRUPTS
-
-
- Your word processor will work on a Compaq, an IBM, an AST or any
- other type of PC compatible computer. Every time it wants to read
- a file from disk or write to a printer, it calls a DOS or BIOS
- function that takes care of it.{1} Yet every computer has its
- own versions of these subroutines, and they not only are
- different, they are in different places in memory. How does the
- word processor know where to find them? It uses interrupts.{2}
-
- An interrupt is a glorified subprogram call. The first 1024 bytes
- of your computer's memory (that's from 0000:0000 to 0000:03FF)
- contain the addresses of all the DOS and BIOS interrupts. Which
- address contains the address of which subprogram was decided by
- the triumverate of Intel/IBM/Microsoft. We have two different
- sets of addresses here, so let's keep them straight. Starting at
- memory address 0000 there are 4 byte addresses called interrupt
- vectors. There is a different vector at 0000d, at 0004d, at
- 0008d, at 0012d at 0016d etc.; there are 256 of them in all. Each
- of these 256 places can hold the address of a subprogram
- somewhere in memory (although not all of them are used).
-
- When you call an interrupt, the 8086 goes to the appropriate
- place in low memory (from 0000 to 3FFh) and finds the address of
- the subprogram that you want, loads it into CS and IP, and goes
- to that subprogram. When that subprogram is done, it goes back to
- the next instruction in your program after the interrupt.
-
- How did those addresses get into the first 1024 bytes? The
- computer put them there when you started it up. It is one of the
- first things that the computer did when you turned it on. These
- subprograms do EVERYTHING, and you can't run the computer without
- them.
-
- If you want to scroll the screen, you put the appropriate
- information in the 8086 registers and use:
-
- int 10h ; decimal 16 {3}
-
- The 8086 goes to the interrupt 16 address (4*16 = 64), gets the
- address of the program that contains the video subprograms, and
- ____________________
-
- 1. BIOS stands for basic input/output services.
-
- 2. If you haven't gotten it yet, it's time to get either "DOS
- Programmer's Reference" or "The Peter Norton Programmer's Guide
- to the IBM PC." I'm serious. They contain the information you
- need to make use of this chapter.
-
- 3. It is normal to use hex numbers for the interrupts, so if
- you read about an interrupt make sure you know if the numbers are
- hex or decimal.
-
- ______________________
-
- The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
-
-
-
-
- The PC Assembler Tutor 178
- ______________________
-
- goes to it. If you want to write to the printer, you put the
- appropriate information in the 8086 registers and call:
-
- int 21h ; decimal 33
-
- The 8086 goes to address 132 (4*33 = 132), gets the address of
- the DOS program, puts it in CS and IP, and starts it. If you want
- to get input from the keyboard, you put the appropriate
- information in the 8086 registers and call:
-
- int 21h ; decimal 33
-
- That's right, it is the same program as the one that does the
- printer. The 8086 goes to 132 (4*33), gets the program address,
- and goes to it.
-
- The lowest interrupt is int 0 (address = 4*0 = 0000). The highest
- interrupt is int 255 (address = 4*255 = 1020).
-
- This is an intelligent way to handle the situation. As long as
- everyone agrees which interrupt contains the address of which
- subprogram, our programs will work on any PC compatible. This is
- one of the things that is meant by PC compatible.
-
- On my computer, here is a section of these addresses starting
- with int 1Eh (30d). High memory is at the top, low memory is at
- the bottom.
-
- INT # DATA LOCATION
-
- 0724h cs 146
- 36 04A8h ip 144
- cs 0724h 142
- 35 ip 01BDh 140
- 0724h cs 138
- 34 01B0h ip 136
- cs 019Fh 134
- 33 ip 05EBh 132
- 019Fh cs 130
- 32 05E7h ip 128
- cs 0000h 126
- 31 ip 0000h 124
- 0070h cs 122
- 30 0EB8h ip 120
-
- If we call int 30d, the 8086 goes to 120 (4*30), puts 0EB8h into
- the IP, and puts 0070h (from the next higher location) into CS.
- The next instruction it does will be 0070:0EB8. If we call int
- 35d, the 8086 goes to 140 (4*35), puts 01BDh in IP, puts 0724h
- (from the next higher location) in CS. The next instruction the
- 8086 does will be 0724:01BD. If we call int 33d, the 8086 goes to
- 132 (4*33), and puts 05EBh in IP, 019Fh (from the next higher
- location) in CS. The next instruction executed will be
- 019F:05EBh. The 0000:0000 for int 31d indicates that there is no
- int 31d (address 0000:0000 contains data, [the vectors for int
- 0], not a program). Make sure that you understand how this is
- working before you go on.
-
-
-
-
- Chapter 17 - Interrupts 179
- _______________________
-
-
- On the PC, information for the interrupts is always passed
- through registers, not on the stack. Each interrupt type has a
- specific register for each piece of information it needs. We will
- do a couple to see how they work.
-
-
- DISPLAYING A CHARACTER
-
- We can print a character at a time on the monitor, so we'll input
- a string, and then print out each character of the string
- individually. We will stop the printout when we see the 0 at the
- end of the string.
-
- ; - - - - - - - - - - START DATA BELOW THIS LINE
- buffer db 80 dup (?)
- ; - - - - - - - - - - START DATA BELOW THIS LINE
- ; - - - - - - - - - - START CODE BELOW THIS LINE
- call show_regs
- outer_loop:
- mov ax, offset buffer
- call get_string
-
- mov si, offset buffer
- inner_loop:
- mov al, [si]
- cmp al, 0
- je next_string
- mov ah, 14 ; ah contains function number
- mov bh, 0 ; where in memory
- int 10h ; 33d
- inc si
- jmp inner_loop
-
- next_string:
- call get_continue
- jmp outer_loop
-
- ; - - - - - - - - - - START CODE BELOW THIS LINE
-
- The program is simple. It gets a string, then checks each
- character for 0 (end of string), before shipping it off to the
- screen. I'll explain AH in a second. There are several places
- this character can be displayed{4}, so use show_regs to force the
- video screen to a certain place in memory, then put BH = 0 to
- tell the interrupt that the screen memory is in that place.
- Finally, with all the information in place, we do the interrupt.
- You will not print a carriage return, only the data, so we use
- get_continue to give us a carriage return. It's a little sloppy,
- but much easier.
-
- We have lots and lots of subprograms for the disks, printer,
- screen, etc. There are only 255 interrupts, so it was decided at
- the beginning to make most of the interrupts groups of programs
- ____________________
-
- 4. Cf. one of those two books.
-
-
-
-
- The PC Assembler Tutor 180
- ______________________
-
- instead of a single program. Int 10h (16d) contains about two
- dozed different video subprograms. Each program is distinguished
- by a specific number in AH. For int 10h (16d):
-
- ah = 2h Get cursor position
- ah = 6h Scroll window up
- ah = Eh (14d) Write character to screen
-
- For int 21h (33d):
-
- ah = 1h Keyboard input
- ah = 5h Printer output
- ah = 17h Rename a file
- ah = 2Ch Get the time
-
- As you can see, int 21h is a potpourri of subprograms. We'll do
- the same program as above, but with printer output. Everything is
- the same except that the inner loop should be changed to look
- like this:
-
- ; - - - - -
- mov si, offset buffer
- inner_loop:
- mov dl, [si]
- cmp dl, 0
- je next_string
- mov ah, 5 ; ah contains function number
- int 21h ; 33d
- inc si
- jmp inner_loop
-
- ; - - - - -
-
- There is practically no change. We use DL instead of AL, have
- "int 21h" instead of "int 10h", and change the function number in
- AH to 5. Also, BH is not needed.
-
- Leave your printer off at the beginning to see what happens. Turn
- your printer on and enter a string of 10 or 20 letters. Probably
- nothing happened. Enter another 20 or 30 letters. Nothing again.
- Try 50 letters this time. This time it should work. Lots of
- printers won't print anything unless (1) they get a carriage
- return (which you haven't sent) or (2) they have a backlog of
- more than 80 characters. If you ever use the printer interrupt,
- you'll have to remember that.
-
- These interrupts which are in your program are called software
- interrupts. They are your interface with the peripheral devices
- on your machine, doing everything from disk i/o to handling
- memory allocation. They do all your housekeeping for you.{5} If
- you are going to work at this level then you should buy one of
- those two books. They contain all the software interrupts (and
- there are about a hundred of them), tell how they work and which
- registers to set for each interrupt. If you don't have access to
- ____________________
-
- 5. But they don't do Windows.
-
-
-
-
- Chapter 17 - Interrupts 181
- _______________________
-
- these interrupts it's like having your arms cut off - you're
- unable to do any i/o at all.
-
-
- HARDWARE INTERRUPTS
-
- The interrupts that we write in our programs aren't the only
- interrupts there are. The hardware uses interrupts to take
- temporary control of the computer. On a Macintosh, if you insert
- a disk, the program stops and the operating system reads in the
- disk directory. A modem that is in use will request time for
- doing i/o. Also, if the 8086 detects a zero divide, it will
- trigger a special interrupt. You have met int 4 already. It is
- the INTO instruction, which will trigger an interrupt if the
- overflow flag is set.
-
- Your interrupts are in specific places in your code, but these
- machine interrupts can happen at any time. There are two lines
- (wires) into the 8086. One is for serious problems that need to
- be taken care of NOW, and it has non-maskable interrupts. The
- other line is for interrupts that need to be taken care of in a
- timely fashion, and they are maskable interrupts.
-
- A non-maskable interrupt (NMI) is when the hardware detects that
- it is in deep doo doo. It sends a signal on the NMI line that
- says "Hey! I need an interrupt." The 8086 finishes the
- instruction it is processing and then IMMEDIATELY gives over
- control. The NMI uses the same 1024 bytes in low memory for
- interrupt vectors, but has its own interrupt numbers. Normally
- this is for very serious errors, so the interrupt program may
- decide to abort your program and return to the operating system;
- if it makes sense to, it will return to your program where your
- program left off.
-
- A maskable interrupt is when a piece of hardware has some work to
- do. It sends a signal to the 8086 (on the INTR line), and the
- 8086 takes care of it when it is ready.
-
- When the 8086 is ready depends on you. In the flags register is
- the IEF, the interrupt enable flag. It should always be set to 1
- unless you are doing something critical. Basically, the only
- things that are critical are interrupts themselves and context
- switches. Context switches are done by the operating system in
- multitasking environments, so they don't concern you, and you are
- not writing interrupts, so they don't concern you. Therefore,
- always keep the IEF set. Just for your information, you set the
- IEF with:
-
- sti ; set interrupt flag (interrupts enabled)
-
- and clear it with:
-
- cli ; clear interrupt flag (interrupts disabled)
-
- Why do interrupt programs clear the interrupt flag? Because you
- could have interrupts interrupting other interrupts and wind up
- with scads of half finished interrupts lying around. This way,
-
-
-
-
- The PC Assembler Tutor 182
- ______________________
-
- one interrupt finishes before another can take over.
-
-
- Suppose you are in the middle of the following two instructions
- when an interrupt hits:
-
- cmp al, 7
- jne some_label
-
- If the hardware interrupt takes over after the CMP instruction
- but before the JNE instruction, the correctness of the result
- will depend on the zero flag staying the same. Can you trust it?
- The answer is yes. The first three things an interrupt request
- does (in the microcode) are:
-
- push flags ; push the flags
- push old CS ; push the code segment
- push old IP ; push the instruction pointer
-
- On return, it pops the flags back in place, so they are exactly
- the same as just before the interrupt. You will notice that there
- are 3 things on the stack instead of the usual 2 in a far call.
- Therefore, there is a special RET instruction called IRET (return
- from interrupt) which pops not only IP and CS, but the flags as
- well. You can only use this instruction in an interrupt because
- it assumes that the flags register is right after CS on the
- stack.
-
-
- DEBUGGING
-
- Finally, if you have a debugger installed, the debugger uses two
- interrupts, int 1 and int 3. You code int 3 into the program by
- placing:
-
- int
-
- in the program with no number.{6} The interrupt will call the
- debugger. The reason for this int with no number is that
- afterwards, you can replace it with NOP, since they are both 1
- byte instructions. Int 3 is a 2 byte instruction (they are coded
- differently, even though they wind up at the same place).
-
- Int 1 is the trap instruction. In the flags register is the trap
- flag (TF). If TF is set, the 8086 will interrupt after the next
- instruction. That way, the debugger can single step through
- sections of code. You put Int 3 to set a breakpoint and then set
- TF to single step from there.
-
- The only way to set TF from your own program is:
-
- ____________________
-
- 6. Though the Microsoft assembler considers this an error. You
- must code it as:
-
- int 3
-
-
-
-
- Chapter 17 - Interrupts 183
- _______________________
-
- pushf
- pop ax
- or ax, 0100h
- push ax
- popf
-
- but you should let the debugger do it.
-
- Just so you can get a feel for how the debugger system works,
- there is a pseudo-debugger called PSEUDDBG.COM in \XTRAFILE. It
- is not a debugger. It does nothing but tell you whether you have
- generated a breakpoint interrupt (int 3) or a single_step
- interrupt (int 1). It is memory resident. That means that once
- you have loaded it, it will stay in memory until you shut the
- machine off or reset the machine. You load it by executing:
-
- >pseuddbg
-
- It will tell you that it has been loaded and then sit there
- waiting for interrupts. When you generate a breakpoint interrupt,
- PSEUDDBG will allow you to set the trap flag or just continue.
- When you generate a single step interrupt, PSEUDDBG will allow
- you to clear TF or to continue single stepping. All you need to
- try it out is a simple program:
-
- ; - - - - - ENTER CODE BELOW THIS LINE
- outer_loop:
- call get_continue
-
- mov cx, 4
- int 3
- inner_loop:
- mov ax, 5
- add ax, 3
- add ax, 7
- add ax, 10
- loop inner_loop
-
- loop outer_loop
- ; - - - - - FINISH CODE ABOVE THIS LINE
-
- Each time you start the outer loop, it will generate a breakpoint
- interrupt. At this point you can set TF to single step or
- continue. If you single step, watch IP. It will be changing,
- cycling through the loop till it gets to get_continue. If you
- start trapping through get_continue, things will get strange
- because get_continue stores the flags. This means that after you
- quit trapping, get_continue will POP some flags that have TF set
- and it will start trapping again. If this happens, just continue
- hitting 0 until you get out of the single step mode.
-
- If you like this method of single stepping through code, there is
- a memory resident version of ASMHELP called HELPMEM.COM which
- contains show_regs and allows single stepping. It can be used in
- conjunction with ASMHELP.OBJ. For details consult APP1.DOC in the
- appendix.
-
-
-
-
-
- The PC Assembler Tutor 184
- ______________________
-
- SUMMARY
-
- Software interrupts send the 8086 to the first 1024 bytes of
- memory where it finds the address of the DOS or BIOS subprogram
- that you want to go to. The correspondence between the interrupt
- number and the location of the address is:
-
- address location = 4 * interrupt number
-
- The address is stored in the next 4 bytes; first IP, then CS.
-
-
-
- The first 5 interrupts are:
-
- int 0 divide by zero
- int 1 trap flag induced single stepping for the debugger
- int 2 non_maskable_interrupt (NMI)
- int 3 1 byte interrupt for the debugger
- int 4 interrupt on overflow
-
-
- There are two types of hardware interrupts. NMI (non maskable
- interrupt) interrupts are for serious problems that need to be
- taken care of IMMEDIATELY. They have priority and take over right
- after the current instruction is finished.
-
- Maskable interrupts are hardware interrupts that should be taken
- care of in a timely fashion. If IEF (the interrupt enable flag)
- is set the maskable interrupts will go through. If IEF is
- cleared, the maskable interrupts must wait until it is set again.
- Set the IEF with:
-
- sti ; set interrupt flag
-
- and clear it with:
-
- cli ; clear interrupt flag
-
- The IEF should always stay set unless there is a specific reason
- for not allowing interrupts to happen.
-
-
- If you are writing an interrupt routine, then instead of RET you
- use IRET (return from interrupt) which not only pops off IP and
- CS, but pops the flags register as well.
-
-